/*
 * by cansik / Florian
 * Datum: 06.05.2010
 * Zeit: 09:49
 * 
 *
 */
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;

namespace PixelReplacer
{
	/// <summary>
	/// Replacer porcesses a manipulation routine
	/// </summary>
	public class Replacer
	{
        /// <summary>
        /// List of Rules
        /// </summary>
		public List<PixelRule> Rules {get; set;}
        /// <summary>
        /// Picture which will be manipulated
        /// </summary>
		public Bitmap Picture{get; set;}
        /// <summary>
        /// Percent Value of finishing status
        /// </summary>
        public int FinishedInPercent { get; private set; }
		
        /// <summary>
        /// Replacer porcesses a manipulation routine
        /// </summary>
		public Replacer()
		{
			Rules = new List<PixelRule>();
		}
		
        /// <summary>
        /// Iterates through every pixel in the image
        /// </summary>
        virtual public void Recognize()
        {
            UnsafeBitmap bmp = new UnsafeBitmap(this.Picture);
            bmp.LockBitmap();

            int pixelcounter = 0;
            for (int x = 0; x < Picture.Width; x++)
            {
                for (int y = 0; y < Picture.Height; y++)
                {
                    Color c = bmp.GetPixel(x, y).ToColor();
                    int index = Match(c);
                    if (index != -1)
                    {
                        //check for unwanted iteration!
                        bmp.SetPixel(x, y, ToPixelData(Handler(c, x, y, Rules[index])));
                    }

                    pixelcounter++;
                    RecalcPercent(pixelcounter);
                }
            }

            bmp.UnlockBitmap();
            this.Picture = bmp.Bitmap;
        }

        private PixelData ToPixelData(Color c)
        {
            PixelData p = new PixelData();
            p.red = c.R;
            p.green = c.G;
            p.blue = c.B;

            return p;
        }

        /// <summary>
        /// Calculates the finishing status
        /// </summary>
        /// <param name="currentcount">Current Pixel number</param>
        protected void RecalcPercent(int currentcount)
        {
            int maxpixel = Picture.Width * Picture.Height;
            int max = Convert.ToInt32(currentcount / (maxpixel / 100));
            this.FinishedInPercent = max;
        }
		
        /// <summary>
        /// Replace the Pixel with the rule values
        /// </summary>
        /// <param name="c">Color of the pixel</param>
        /// <param name="x">X coordinate of the Pixel</param>
        /// <param name="y">Y coordinate of the Pixel</param>
        /// <param name="rule">Pixel Rule</param>
        /// <returns>Calculated Color</returns>
		virtual protected Color Handler(Color c, int x, int y, PixelRule rule)
		{
            Color currp = c;
			Color finalcolor = rule.NewColor;
			
			if (rule.AdaptColors)
			{
				finalcolor =  AdaptColor(currp, finalcolor);
			}

			return finalcolor;
		}

        /// <summary>
        /// Does mix the old color with the new color
        /// </summary>
        /// <param name="originalcolor">Old Color</param>
        /// <param name="newcolor">New Color</param>
        /// <returns>Mixed Color</returns>
        protected Color AdaptColor(Color originalcolor, Color newcolor)
        {
            //create the grayscale version of the pixels
            float grayScaleOriginal = (int)((originalcolor.R * .3) + (originalcolor.G * .59) + (originalcolor.B * .11));
            float grayScaleNew = (int)((newcolor.R * .3) + (newcolor.G * .59) + (newcolor.B * .11));

            float deltagray = grayScaleNew - grayScaleOriginal;

            int r = CheckColorBounds(Convert.ToInt32(newcolor.R - deltagray));
            int g = CheckColorBounds(Convert.ToInt32(newcolor.G - deltagray));
            int b = CheckColorBounds(Convert.ToInt32(newcolor.B - deltagray));

            return Color.FromArgb(r, g, b);
        }

        /// <summary>
        /// Checks if the Color Value is a possible value
        /// </summary>
        /// <param name="colorval">Value to Check</param>
        /// <returns>A possible value</returns>
        private int CheckColorBounds(int colorval)
        {
            if (colorval > 255)
            {
                return 255;
            }
            else if (colorval < 0)
            {
                return 0;
            }
            else
            {
                return colorval;
            }
        }
		
        /// <summary>
        /// Checks, if the Color does Macht with a rule
        /// </summary>
        /// <param name="checkColor">Color that shoul'd be checked</param>
        /// <returns>The Number of the Rule</returns>
		protected int Match(Color checkColor)
		{
			int retIndex = -1;
			
			for (int i = 0; i < Rules.Count; i++)
			{
				if (Rules[i].Match(checkColor))
				{
					retIndex = i;
					break;
				}
			}
			
			return retIndex;
		}
	}
}
